Skip to content

Conversation

@nazabucciarelli
Copy link
Contributor

@nazabucciarelli nazabucciarelli commented Jan 22, 2026

Proposed changes (including videos or screenshots)

After some investigation, context gathering, and a discussion with Diego Sampaio, we aligned on the scope of this task.

The goal is to improve how DDP connection headers are handled in sauMonitorHooks.ts.
While the broader issue involves multiple places where the full connection object is passed downstream instead of only the required data, addressing all of those cases is outside the scope of this task.

Changes:

  • To make the change safer and more explicit, I will introduce dedicated types for the event payloads instead of passing the entire connection object.
  • SAU events where renamed adding 'sau.' as prefix in order to avoid confusion with already existent events, such as 'accounts.login' or 'accounts.logout'.
  • As I'm funneling the properties we pass downstream in the hook, the lower level functions where simplified, removing additional logic and making it even more readable.
  • getHeaders() function was added to extract headers in a safer way, regardless the connection was through HTTP or DDP.
  • _getConnectionInfo was removed because it was a thin wrapper with no transformation or validation logic.
    Keeping the object construction at the call site improves traceability of fields (e.g. loginToken, instanceId, httpHeaders) and reduces unnecessary indirection.
  • Improved overall code readability.

Issue(s)

CORE-1529 - Improve DDP header handling

Steps to test or reproduce

Further comments

Please keep in mind that this PR touches sensitive code, such as sessions and login/logout.

Summary by CodeRabbit

  • Refactor

    • Authentication and socket events standardized to compact, strongly-typed payloads (new sau.* event names) and simplified device-login payloads (userAgent, loginToken, clientAddress).
  • Bug Fixes

    • Logout and session completion now use explicit session identifiers; UA and IP consistently sourced from event payloads.
  • New Features

    • Added a reusable header-extraction utility exported for consumers.
  • Tests

    • Added tests covering header extraction edge cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 22, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 22, 2026

⚠️ No Changeset found

Latest commit: 497e50d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

Walkthrough

Server event flow refactored to emit strongly-typed, sau-prefixed events (login/logout/socket) with explicit payload fields (userAgent, loginToken, clientAddress, instanceId, connectionId, host). SAUMonitor and device-management consume these payloads and parse UA strings directly. A new getHeader utility safely extracts header values.

Changes

Cohort / File(s) Summary
SAU Event Types & Emitters
apps/meteor/server/services/sauMonitor/events.ts, apps/meteor/server/services/sauMonitor/service.ts
Introduce sau.* event names and explicit payload types (userId, instanceId, connectionId, loginToken?, clientAddress, userAgent, host); replace ISocketConnection-based payloads and update emitters.
Hooks / Event Producers
apps/meteor/server/hooks/sauMonitorHooks.ts
Login/logout/socket hooks now compute and emit typed payloads (sau.accounts.login, sau.accounts.logout, sau.socket.connected, sau.socket.disconnected) without embedding httpHeaders.
SAU Monitor Implementation
apps/meteor/app/statistics/server/lib/SAUMonitor.ts
_handleSession signature changed to accept HandleSessionArgs and params; UA parsing moved to _getUserAgentInfo(uaString); removed dependency on connection object and _getConnectionInfo.
Device Management Events & Service
apps/meteor/server/services/device-management/events.ts, apps/meteor/server/services/device-management/service.ts, apps/meteor/ee/server/lib/deviceManagement/session.ts
device-login payload changed to { userId, userAgent, loginToken?, clientAddress }; listeners and emitters destructure these fields and parse UA from provided string.
Header Utility
packages/tools/src/getHeader.ts, packages/tools/src/getHeader.spec.ts, packages/tools/src/index.ts
Add getHeader utility (handles string

Sequence Diagram(s)

sequenceDiagram
  participant Hooks as Hooks (sauMonitorHooks)
  participant SauService as sau/service
  participant SAUMonitor as SAUMonitor
  participant DeviceMgmt as Device Management

  Hooks->>SauService: emit sau.accounts.login {userId, instanceId, connectionId, loginToken?, clientAddress, userAgent, host}
  SauService->>SAUMonitor: forward sau.accounts.login payload
  SAUMonitor->>SAUMonitor: _getUserAgentInfo(userAgent) -> device info
  SAUMonitor->>SAUMonitor: _handleSession({userId,...}, params) -> persist/update session

  Hooks->>SauService: emit sau.socket.disconnected {instanceId, connectionId}
  SauService->>SAUMonitor: forward sau.socket.disconnected
  SAUMonitor->>SAUMonitor: finish sessions for connectionId

  SauService->>DeviceMgmt: emit device-login {userId, userAgent, loginToken?, clientAddress}
  DeviceMgmt->>DeviceMgmt: parse UA, send notifications/emails
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • sampaiodiego
  • ggazzo
  • cardoso

Poem

🐰 I hopped through events, neat and spry,
I pulled the UA from a single sky,
Payloads now tidy, headers kept small,
Sessions tracked cleanly, one and all,
A rabbit cheers — the monitor's sigh!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main objective: improving how DDP connection data is sent to hooks, which aligns with the primary changes across all modified files.
Linked Issues check ✅ Passed The PR implements the main objective from CORE-1529 by refactoring how DDP connection headers are handled, introducing typed payloads, renaming SAU events with 'sau.' prefix, and removing connection object dependencies throughout the codebase.
Out of Scope Changes check ✅ Passed All changes are directly related to improving DDP header handling and removing connection object dependencies; no unrelated modifications were introduced outside the specified objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/ddp-headers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nazabucciarelli
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/app/statistics/server/lib/SAUMonitor.ts (1)

135-142: Update logout log messages to the new event name.

The warning strings still mention 'accounts.logout', which no longer matches the hook name and can mislead debugging.

🔧 Suggested fix
-			if (!userId) {
-				logger.warn({ msg: "Received 'accounts.logout' event without 'userId'" });
+			if (!userId) {
+				logger.warn({ msg: "Received 'sau.accounts.logout' event without 'userId'" });
 				return;
 			}
 
 			if (!sessionId) {
-				logger.warn({ msg: "Received 'accounts.logout' event without 'sessionId'" });
+				logger.warn({ msg: "Received 'sau.accounts.logout' event without 'sessionId'" });
 				return;
 			}
🤖 Fix all issues with AI agents
In `@packages/core-typings/src/sau/SocketConnectedPayload.ts`:
- Around line 1-3: Remove the inline comment inside the SocketConnectedPayload
type and replace the placeholder with an explicit empty payload type;
specifically, update export type SocketConnectedPayload to an explicit empty
shape (e.g., use Record<string, never> or an empty object type) so the intent is
encoded in the type rather than a comment and the inline comment is removed.
🧹 Nitpick comments (3)
apps/meteor/server/services/sauMonitor/service.ts (1)

35-38: Remove commented-out debug statement.

Per coding guidelines, avoid code comments in the implementation. This commented-out console.log should be removed.

Suggested fix
 		this.onEvent('socket.connected', async (data) => {
-			// console.log('socket.connected', data);
 			sauEvents.emit('sau.socket.connected', data);
 		});
apps/meteor/server/services/device-management/service.ts (1)

14-19: Address TODO: Add loginToken to payload.

The TODO indicates loginToken should be added to the device-login payload. The DeviceLoginPayload type already supports an optional loginToken field (as seen in session.ts usage at line 42).

Would you like me to help implement the loginToken extraction similar to how it's done in sauMonitorHooks.ts (using Accounts._hashLoginToken(resume))? Or should I open an issue to track this task?

apps/meteor/server/hooks/sauMonitorHooks.ts (1)

80-86: Remove inline comment and clarify SocketConnectedPayload intent.

The comment on line 82 should be removed per coding guidelines. Additionally, spreading the entire connection object into SocketConnectedPayload appears to contradict the PR's goal of avoiding passing full connection objects downstream. Consider extracting only the required fields.

Suggested fix

If only specific fields are needed, extract them explicitly:

 Meteor.onConnection((connection) => {
 	const socketConnectedEventObject: SocketConnectedPayload = {
-		// Implement SocketConnectedPayload type in case of using the sau.socket.connected hook
-		...connection,
+		connectionId: connection.id,
+		// Add other required fields as needed
 	};
 	sauEvents.emit('sau.socket.connected', socketConnectedEventObject);
 });

If the full connection is intentionally needed here, the comment should at minimum be removed.

@codecov
Copy link

codecov bot commented Jan 22, 2026

Codecov Report

❌ Patch coverage is 78.37838% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.78%. Comparing base (5650b78) to head (fa604e3).
⚠️ Report is 4 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #38294      +/-   ##
===========================================
+ Coverage    70.77%   70.78%   +0.01%     
===========================================
  Files         3159     3160       +1     
  Lines       109384   109420      +36     
  Branches     19644    19669      +25     
===========================================
+ Hits         77412    77457      +45     
+ Misses       29939    29933       -6     
+ Partials      2033     2030       -3     
Flag Coverage Δ
e2e 60.34% <ø> (-0.01%) ⬇️
e2e-api 47.87% <55.55%> (-0.17%) ⬇️
unit 71.98% <100.00%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

📦 Docker Image Size Report

➡️ Changes

Service Current Baseline Change Percent
sum of all images 0B 0B 0B
account-service 0B 0B 0B
authorization-service 0B 0B 0B
ddp-streamer-service 0B 0B 0B
omnichannel-transcript-service 0B 0B 0B
presence-service 0B 0B 0B
queue-worker-service 0B 0B 0B
rocketchat 0B 0B 0B

📊 Historical Trend

---
config:
  theme: "dark"
  xyChart:
    width: 900
    height: 400
---
xychart
  title "Image Size Evolution by Service (Last 30 Days + This PR)"
  x-axis ["11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 21:04", "12/18 23:12", "12/19 23:27", "12/20 21:03", "12/22 18:54", "12/23 16:16", "12/24 19:38", "12/25 17:51", "12/26 13:18", "12/29 19:01", "12/30 20:52", "01/27 17:29 (PR)"]
  y-axis "Size (GB)" 0 --> 0.5
  line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.00]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "queue-worker-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "rocketchat" [0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.00]
Loading

Statistics (last 30 days):

  • 📊 Average: 1.5GiB
  • ⬇️ Minimum: 1.4GiB
  • ⬆️ Maximum: 1.6GiB
  • 🎯 Current PR: 0B
ℹ️ About this report

This report compares Docker image sizes from this build against the develop baseline.

  • Tag: pr-38294
  • Baseline: develop
  • Timestamp: 2026-01-27 17:29:06 UTC
  • Historical data points: 30

Updated: Tue, 27 Jan 2026 17:29:07 GMT

@nazabucciarelli
Copy link
Contributor Author

nazabucciarelli commented Jan 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/app/statistics/server/lib/SAUMonitor.ts (1)

235-245: Replace the inline TODO block with an explicit UA result type.

The comment block violates the "no implementation comments" guideline. Move the type information into code as a type alias instead.

However, the proposed solution using ReturnType<typeof UAParserMobile.uaObject> won't work as expected since UAParserCustom.js is untyped JavaScript. Define the union type explicitly instead:

♻️ Suggested approach
+	type UAResult =
+		| UAParser.IResult
+		| { device?: { type: string }; app?: { name: string; version: string }; browser?: undefined; os?: undefined }
+		| { device?: { type: string }; os?: Record<string, string>; app?: { name: string; version: string }; browser?: undefined };
+
-	const result = ((): any => {
+	const result = ((): UAResult => {

The types correspond to the three code branches: standard UAParser, mobile app, and desktop app respectively.

♻️ Duplicate comments (1)
packages/core-typings/src/sau/SocketConnectedPayload.ts (1)

1-2: Remove inline comment; encode empty payload in the type.

Line 1 violates the “no implementation comments” guideline; encode the empty payload via the type instead. As per coding guidelines, avoid code comments in implementation.

♻️ Proposed change
-export type SocketConnectedPayload = {};
+export type SocketConnectedPayload = Record<string, never>;
Is `Record<string, never>` the recommended TypeScript pattern for expressing an empty object payload?

@nazabucciarelli nazabucciarelli marked this pull request as ready for review January 23, 2026 16:47
@nazabucciarelli nazabucciarelli requested review from a team as code owners January 23, 2026 16:47
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 14 files

@nazabucciarelli
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/meteor/server/services/sauMonitor/events.ts`:
- Around line 4-12: The event type for 'sau.accounts.login' is inconsistent with
ISocketConnection because connection.loginToken is optional; update the event
definition in events.ts to make loginToken optional (change loginToken: string
to loginToken?: string) so it matches ISocketConnection and avoids the type
contract violation, then recompile/typecheck; alternatively, if you prefer
forcing a string, update the caller in service.ts where connection.loginToken is
passed to provide a default (e.g., connection.loginToken ?? '')—pick one
approach and apply it consistently across sau.accounts.login, service.ts, and
references in sauMonitorHooks.ts.
🧹 Nitpick comments (3)
apps/meteor/server/services/sauMonitor/service.ts (1)

42-45: Remove commented-out debug code.

The commented console.log statement should be removed to keep the codebase clean.

♻️ Proposed fix
 		this.onEvent('socket.connected', async (data) => {
-			// console.log('socket.connected', data);
 			sauEvents.emit('sau.socket.connected', { instanceId: InstanceStatus.id(), connectionId: data.id });
 		});
apps/meteor/server/hooks/sauMonitorHooks.ts (1)

49-58: Consider consolidating the two Meteor.onConnection handlers.

Both handlers register on the same connection event. Merging them would reduce overhead and improve code organization.

♻️ Proposed consolidation
 Meteor.onConnection((connection) => {
+	// in case of implementing a listener of this event, define the parameters type in services/sauMonitor/events.ts
+	sauEvents.emit('sau.socket.connected', { instanceId: InstanceStatus.id(), connectionId: connection.id });
+
 	connection.onClose(async () => {
 		sauEvents.emit('sau.socket.disconnected', { connectionId: connection.id, instanceId: InstanceStatus.id() });
 	});
 });
-
-Meteor.onConnection((connection) => {
-	// in case of implementing a listener of this event, define the parameters type in services/sauMonitor/events.ts
-	sauEvents.emit('sau.socket.connected', { instanceId: InstanceStatus.id(), connectionId: connection.id });
-});
apps/meteor/app/statistics/server/lib/SAUMonitor.ts (1)

238-307: Return type includes undefined but function always returns a value.

The function always returns { device: info } (lines 304-306), but the return type is { device: ISessionDevice } | undefined. This could lead to unnecessary null checks by callers.

♻️ Proposed fix
-	private _getUserAgentInfo(uaString: string): { device: ISessionDevice } | undefined {
+	private _getUserAgentInfo(uaString: string): { device: ISessionDevice } {

@nazabucciarelli nazabucciarelli marked this pull request as draft January 23, 2026 21:45
@nazabucciarelli nazabucciarelli marked this pull request as ready for review January 26, 2026 17:44
@nazabucciarelli
Copy link
Contributor Author

nazabucciarelli commented Jan 26, 2026

@coderabbitai review

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 8 files

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@@ -0,0 +1,11 @@
export const getHeader = (headers: unknown, key: string): string => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be in the tools package.

It would also be nice to have unit tests for it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nazabucciarelli and others added 2 commits January 26, 2026 16:34
@nazabucciarelli nazabucciarelli requested a review from a team as a code owner January 26, 2026 22:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/meteor/server/hooks/sauMonitorHooks.ts`:
- Line 22: Replace the private Meteor call Accounts._hashLoginToken with the
public hashLoginToken from `@rocket.chat/account-utils`: import { hashLoginToken }
and change the expression that computes loginToken (currently using
Accounts._hashLoginToken(resume)) to use hashLoginToken(resume) (preserving the
ternary behavior with resume). Update any existing imports to add hashLoginToken
and remove reliance on Accounts._hashLoginToken so the code computes loginToken
via the public API.
- Line 38: Remove the duplicate device-login emission in sauMonitorHooks by
deleting the deviceManagementEvents.emit('device-login', { userId:
info.user._id, userAgent, loginToken, clientAddress }) call, and instead update
DeviceManagementService (the service that listens to accounts.login and emits
'device-login') to include the loginToken in its emitted payload so the single
emission covers the session handler in
apps/meteor/ee/server/lib/deviceManagement/session.ts; ensure the emitted object
from DeviceManagementService contains userId, userAgent, clientAddress and
loginToken to match the original payload.
🧹 Nitpick comments (1)
packages/tools/src/getHeader.ts (1)

3-3: Consider exporting HeaderLike type for consumers.

Consumers of getHeader might benefit from having access to the HeaderLike type alias for their own type annotations. This is optional since the function's parameter typing provides guidance.

♻️ Optional change
-type HeaderLike = IncomingHttpHeaders | Record<string, string | string[] | undefined>;
+export type HeaderLike = IncomingHttpHeaders | Record<string, string | string[] | undefined>;

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 5 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/tools/src/getHeader.ts">

<violation number="1" location="packages/tools/src/getHeader.ts:5">
P1: `getHeader` now uses a generic return type with `as T` casts, but it does not guarantee the runtime value matches `T` (e.g., `T=string[]` still returns a string for `Headers`, and the default mode can now return `string[]` for Node headers). This is both a type-safety issue and a potential runtime regression for callers that expect a string.</violation>
</file>

<file name="apps/meteor/server/hooks/sauMonitorHooks.ts">

<violation number="1" location="apps/meteor/server/hooks/sauMonitorHooks.ts:24">
P2: `clientAddress` is a required `string`, so it may be `''` when unknown. Using `??` won’t fall back to `x-real-ip` in that case (behavior change from the previous `getClientAddress` helper which used `||`). Use `||` to preserve the fallback when `clientAddress` is an empty string.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +5 to +15
export const getHeader = <T extends string | string[] = string>(headers: HeaderLike, key: string): T => {
if (!headers) {
return '' as T;
}

if (headers instanceof Headers) {
return (headers.get(key) ?? '') as T;
}

return (headers[key] ?? '') as T;
};
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: getHeader now uses a generic return type with as T casts, but it does not guarantee the runtime value matches T (e.g., T=string[] still returns a string for Headers, and the default mode can now return string[] for Node headers). This is both a type-safety issue and a potential runtime regression for callers that expect a string.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/tools/src/getHeader.ts, line 5:

<comment>`getHeader` now uses a generic return type with `as T` casts, but it does not guarantee the runtime value matches `T` (e.g., `T=string[]` still returns a string for `Headers`, and the default mode can now return `string[]` for Node headers). This is both a type-safety issue and a potential runtime regression for callers that expect a string.</comment>

<file context>
@@ -2,34 +2,14 @@ import type { IncomingHttpHeaders } from 'http';
-export function getHeader(headers: HeaderLike, key: string, asArray: true): string[];
-
-export function getHeader(headers: HeaderLike, key: string, asArray = false): string | string[] {
+export const getHeader = <T extends string | string[] = string>(headers: HeaderLike, key: string): T => {
 	if (!headers) {
-		return asArray ? [] : '';
</file context>
Suggested change
export const getHeader = <T extends string | string[] = string>(headers: HeaderLike, key: string): T => {
if (!headers) {
return '' as T;
}
if (headers instanceof Headers) {
return (headers.get(key) ?? '') as T;
}
return (headers[key] ?? '') as T;
};
export function getHeader(headers: HeaderLike | undefined, key: string): string;
export function getHeader(headers: HeaderLike | undefined, key: string, asArray: true): string[];
export function getHeader(headers: HeaderLike | undefined, key: string, asArray = false): string | string[] {
if (!headers) {
return asArray ? [] : '';
}
const value = headers instanceof Headers ? (headers.get(key) ?? undefined) : headers[key];
if (Array.isArray(value)) {
return asArray ? value : (value[0] ?? '');
}
if (typeof value === 'string') {
return asArray ? [value] : value;
}
return asArray ? [] : '';
}
Fix with Cubic

const { resume } = methodArguments.find((arg) => 'resume' in arg) ?? {};
const loginToken = resume ? hashLoginToken(resume) : '';
const instanceId = InstanceStatus.id();
const clientAddress = info.connection.clientAddress ?? getHeader(httpHeaders, 'x-real-ip');
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: clientAddress is a required string, so it may be '' when unknown. Using ?? won’t fall back to x-real-ip in that case (behavior change from the previous getClientAddress helper which used ||). Use || to preserve the fallback when clientAddress is an empty string.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/server/hooks/sauMonitorHooks.ts, line 24:

<comment>`clientAddress` is a required `string`, so it may be `''` when unknown. Using `??` won’t fall back to `x-real-ip` in that case (behavior change from the previous `getClientAddress` helper which used `||`). Use `||` to preserve the fallback when `clientAddress` is an empty string.</comment>

<file context>
@@ -22,7 +21,7 @@ Accounts.onLogin((info: ILoginAttempt) => {
 	const loginToken = resume ? hashLoginToken(resume) : '';
 	const instanceId = InstanceStatus.id();
-	const clientAddress = getClientAddress(info.connection);
+	const clientAddress = info.connection.clientAddress ?? getHeader(httpHeaders, 'x-real-ip');
 	const userAgent = getHeader(httpHeaders, 'user-agent');
 	const host = getHeader(httpHeaders, 'host');
</file context>
Suggested change
const clientAddress = info.connection.clientAddress ?? getHeader(httpHeaders, 'x-real-ip');
const clientAddress = info.connection.clientAddress || getHeader(httpHeaders, 'x-real-ip');
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants